///**
// * Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
// * <p>
// * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
// * <p>
// * 1. Definitions.
// * <p>
// * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined
// * by Sections 1 through 9 of this document.
// * <p>
// * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is
// * granting the License.
// * <p>
// * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are
// * controlled by, or are under common control with that entity. For the purposes of this definition,
// * "control" means (i) the power, direct or indirect, to cause the direction or management of such
// * entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
// * outstanding shares, or (iii) beneficial ownership of such entity.
// * <p>
// * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this
// * License.
// * <p>
// * "Source" form shall mean the preferred form for making modifications, including but not limited
// * to software source code, documentation source, and configuration files.
// * <p>
// * "Object" form shall mean any form resulting from mechanical transformation or translation of a
// * Source form, including but not limited to compiled object code, generated documentation, and
// * conversions to other media types.
// * <p>
// * "Work" shall mean the work of authorship, whether in Source or Object form, made available under
// * the License, as indicated by a copyright notice that is included in or attached to the work (an
// * example is provided in the Appendix below).
// * <p>
// * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or
// * derived from) the Work and for which the editorial revisions, annotations, elaborations, or other
// * modifications represent, as a whole, an original work of authorship. For the purposes of this
// * License, Derivative Works shall not include works that remain separable from, or merely link (or
// * bind by name) to the interfaces of, the Work and Derivative Works thereof.
// * <p>
// * "Contribution" shall mean any work of authorship, including the original version of the Work and
// * any modifications or additions to that Work or Derivative Works thereof, that is intentionally
// * submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or
// * Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this
// * definition, "submitted" means any form of electronic, verbal, or written communication sent to
// * the Licensor or its representatives, including but not limited to communication on electronic
// * mailing lists, source code control systems, and issue tracking systems that are managed by, or on
// * behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding
// * communication that is conspicuously marked or otherwise designated in writing by the copyright
// * owner as "Not a Contribution."
// * <p>
// * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a
// * Contribution has been received by Licensor and subsequently incorporated within the Work.
// * <p>
// * 2. Grant of Copyright License. Subject to the terms and conditions of this License, each
// * Contributor hereby grants to You a perpetual, worldwide, non exclusive, no charge, royalty free,
// * irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display,
// * publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or
// * Object form.
// * <p>
// * 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor
// * hereby grants to You a perpetual, worldwide, non exclusive, no charge, royalty free, irrevocable
// * (except as stated in this section) patent license to make, have made, use, offer to sell, sell,
// * import, and otherwise transfer the Work, where such license applies only to those patent claims
// * licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or
// * by combination of their Contribution(s) with the Work to which such Contribution(s) was
// * submitted. If You institute patent litigation against any entity (including a cross claim or
// * counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work
// * constitutes direct or contributory patent infringement, then any patent licenses granted to You
// * under this License for that Work shall terminate as of the date such litigation is filed.
// * <p>
// * 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works
// * thereof in any medium, with or without modifications, and in Source or Object form, provided that
// * You meet the following conditions:
// * <p>
// * (a) You must give any other recipients of the Work or Derivative Works a copy of this License;
// * and
// * <p>
// * (b) You must cause any modified files to carry prominent notices stating that You changed the
// * files; and
// * <p>
// * (c) You must retain, in the Source form of any Derivative Works that You distribute, all
// * copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding
// * those notices that do not pertain to any part of the Derivative Works; and
// * <p>
// * (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative
// * Works that You distribute must include a readable copy of the attribution notices contained
// * within such NOTICE file, excluding those notices that do not pertain to any part of the
// * Derivative Works, in at least one of the following places: within a NOTICE text file distributed
// * as part of the Derivative Works; within the Source form or documentation, if provided along with
// * the Derivative Works; or, within a display generated by the Derivative Works, if and wherever
// * such third party notices normally appear. The contents of the NOTICE file are for informational
// * purposes only and do not modify the License. You may add Your own attribution notices within
// * Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the
// * Work, provided that such additional attribution notices cannot be construed as modifying the
// * License.
// * <p>
// * You may add Your own copyright statement to Your modifications and may provide additional or
// * different license terms and conditions for use, reproduction, or distribution of Your
// * modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and
// * distribution of the Work otherwise complies with the conditions stated in this License.
// * <p>
// * 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution
// * intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms
// * and conditions of this License, without any additional terms or conditions. Notwithstanding the
// * above, nothing herein shall supersede or modify the terms of any separate license agreement you
// * may have executed with Licensor regarding such Contributions.
// * <p>
// * 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service
// * marks, or product names of the Licensor, except as required for reasonable and customary use in
// * describing the origin of the Work and reproducing the content of the NOTICE file.
// * <p>
// * 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor
// * provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT
// * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation,
// * any warranties or conditions of TITLE, NON INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
// * PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or
// * redistributing the Work and assume any risks associated with Your exercise of permissions under
// * this License.
// * <p>
// * 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including
// * negligence), contract, or otherwise, unless required by applicable law (such as deliberate and
// * grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for
// * damages, including any direct, indirect, special, incidental, or consequential damages of any
// * character arising as a result of this License or out of the use or inability to use the Work
// * (including but not limited to damages for loss of goodwill, work stoppage, computer failure or
// * malfunction, or any and all other commercial damages or losses), even if such Contributor has
// * been advised of the possibility of such damages.
// * <p>
// * 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works
// * thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty,
// * indemnity, or other liability obligations and/or rights consistent with this License. However, in
// * accepting such obligations, You may act only on Your own behalf and on Your sole responsibility,
// * not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each
// * Contributor harmless for any liability incurred by, or claims asserted against, such Contributor
// * by reason of your accepting any such warranty or additional liability.
// * <p>
// * END OF TERMS AND CONDITIONS
// * <p>
// * APPENDIX: How to apply the Apache License to your work.
// * <p>
// * To apply the Apache License to your work, attach the following boilerplate notice, with the
// * fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include
// * the brackets!)  The text should be enclosed in the appropriate comment syntax for the file
// * format. We also recommend that a file or class name and description of purpose be included on the
// * same "printed page" as the copyright notice for easier identification within third party
// * archives.
// * <p>
// * Copyright 2016 Alibaba Group
// * <p>
// * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// * in compliance with the License. You may obtain a copy of the License at
// * <p>
// * http://www.apache.org/licenses/LICENSE 2.0
// * <p>
// * Unless required by applicable law or agreed to in writing, software distributed under the License
// * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// * or implied. See the License for the specific language governing permissions and limitations under
// * the License.
// */
//package com.benmu.framework.extend.dom.richtext;
//
//import android.graphics.Canvas;
//import android.graphics.Typeface;
//import android.os.Build;
//import android.support.annotation.NonNull;
//import android.support.annotation.Nullable;
//import android.text.Layout;
//import android.text.Spannable;
//import android.text.SpannableString;
//import android.text.Spanned;
//import android.text.StaticLayout;
//import android.text.TextPaint;
//import android.text.TextUtils;
//import android.text.style.AbsoluteSizeSpan;
//import android.text.style.AlignmentSpan;
//import android.text.style.CharacterStyle;
//import android.text.style.ForegroundColorSpan;
//import android.text.style.StrikethroughSpan;
//import android.text.style.UnderlineSpan;
//import android.util.Log;
//
//import com.benmu.framework.extend.comoponents.BMRich;
//import com.taobao.weex.WXEnvironment;
//import com.taobao.weex.WXSDKManager;
//import com.taobao.weex.common.Constants;
//import com.taobao.weex.dom.WXCustomStyleSpan;
//import com.taobao.weex.dom.WXDomObject;
//import com.taobao.weex.dom.WXLineHeightSpan;
//import com.taobao.weex.dom.WXStyle;
//import com.taobao.weex.dom.flex.CSSConstants;
//import com.taobao.weex.dom.flex.CSSNode;
//import com.taobao.weex.dom.flex.FloatUtil;
//import com.taobao.weex.dom.flex.MeasureOutput;
//import com.taobao.weex.ui.component.WXComponent;
//import com.taobao.weex.ui.component.WXText;
//import com.taobao.weex.ui.component.WXTextDecoration;
//import com.taobao.weex.utils.WXDomUtils;
//import com.taobao.weex.utils.WXLogUtils;
//import com.taobao.weex.utils.WXResourceUtils;
//
//import java.util.Collections;
//import java.util.LinkedList;
//import java.util.List;
//import java.util.Map;
//import java.util.concurrent.atomic.AtomicReference;
//
//import static com.taobao.weex.dom.WXStyle.UNSET;
//
///**
// * Created by Carry on 2017/6/16.
// */
//
//public class RichTextDomObject extends WXDomObject {
//    /**
//     * Object for calculating text's width and height. This class is an anonymous class of
//     * implementing {@link MeasureFunction}
//     */
//    /**
//     * package
//     **/
//    static final MeasureFunction TEXT_MEASURE_FUNCTION = new MeasureFunction() {
//        @Override
//        public void measure(CSSNode node, float width, @NonNull MeasureOutput measureOutput) {
//            RichTextDomObject textDomObject = (RichTextDomObject) node;
//            if (CSSConstants.isUndefined(width)) {
//                width = node.cssstyle.maxWidth;
//            }
//            if (textDomObject.getTextWidth(textDomObject.mTextPaint, width, false) > 0) {
//                textDomObject.layout = textDomObject.createLayout(width, false, null);
//                textDomObject.hasBeenMeasured = true;
//                textDomObject.previousWidth = textDomObject.layout.getWidth();
//                measureOutput.height = textDomObject.layout.getHeight();
//                measureOutput.width = textDomObject.previousWidth;
//            } else {
//                measureOutput.height = 0;
//                measureOutput.width = 0;
//            }
//        }
//    };
//
//    private static class SetSpanOperation {
//
//        protected final int start, end, flag;
//        protected final Object what;
//
//        SetSpanOperation(int start, int end, Object what) {
//            this(start, end, what, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//        }
//
//        SetSpanOperation(int start, int end, Object what, int flag) {
//            this.start = start;
//            this.end = end;
//            this.what = what;
//            this.flag = flag;
//        }
//
//        public void execute(Spannable sb) {
//            sb.setSpan(what, start, end, flag);
//        }
//    }
//
//
//    private static final Canvas DUMMY_CANVAS = new Canvas();
//    private static final String ELLIPSIS = "\u2026";
//    private boolean mIsColorSet = false;
//    private boolean hasBeenMeasured = false;
//    private int mColor;
//    /**
//     * mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}.
//     */
//    private int mFontStyle = UNSET;
//    /**
//     * mFontWeight can be {@link Typeface#NORMAL} or {@link Typeface#BOLD}.
//     */
//    private int mFontWeight = UNSET;
//    private int mNumberOfLines = UNSET;
//    private int mFontSize = UNSET;
//    private int mLineHeight = UNSET;
//    private float previousWidth = Float.NaN;
//    private String mFontFamily = null;
//    private String mText = null;
//    private TextUtils.TruncateAt textOverflow;
//    private Layout.Alignment mAlignment;
//    private WXTextDecoration mTextDecoration = WXTextDecoration.NONE;
//    private TextPaint mTextPaint = new TextPaint();
//    private
//    @Nullable
//    Spanned spanned;
//    private
//    @Nullable
//    Layout layout;
//    private AtomicReference<Layout> atomicReference = new AtomicReference<>();
//
//    /**
//     * Create an instance of current class, and set {@link #TEXT_MEASURE_FUNCTION} as the
//     * measureFunction
//     *
//     * @see CSSNode#setMeasureFunction(MeasureFunction)
//     */
//    public RichTextDomObject() {
//        super();
//        mTextPaint.setFlags(TextPaint.ANTI_ALIAS_FLAG);
//        setMeasureFunction(TEXT_MEASURE_FUNCTION);
//    }
//
//    public TextPaint getTextPaint() {
//        return mTextPaint;
//    }
//
//    /**
//     * Prepare the text {@link Spanned} for calculating text's size. This is done by setting various
//     * text span to the text.
//     *
//     * @see CharacterStyle
//     */
//    @Override
//    public void layoutBefore() {
//        hasBeenMeasured = false;
//        updateStyleAndText();
//        mText = getText();
//        spanned = createSpanned(mText);
//        super.dirty();
//        super.layoutBefore();
//    }
//
//    @Override
//    public void layoutAfter() {
//        if (hasBeenMeasured) {
//            if (layout != null &&
//                    !FloatUtil.floatsEqual(WXDomUtils.getContentWidth(this), previousWidth)) {
//                recalculateLayout();
//            }
//        } else {
//            updateStyleAndText();
//            recalculateLayout();
//        }
//        hasBeenMeasured = false;
//        if (layout != null && !layout.equals(atomicReference.get()) &&
//                Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//            //TODO Warm up, a profile should be used to see the improvement.
//            warmUpTextLayoutCache(layout);
//        }
//        swap();
//        super.layoutAfter();
//    }
//
//    @Override
//    public Layout getExtra() {
//        return atomicReference.get();
//    }
//
//    @Override
//    public void updateAttr(Map<String, Object> attrs) {
//        swap();
//        super.updateAttr(attrs);
//        mText = getText();
//    }
//
//    @Override
//    public void updateStyle(Map<String, Object> styles) {
//        swap();
//        super.updateStyle(styles);
//        updateStyleImp(styles);
//    }
//
//    @Override
//    public RichTextDomObject clone() {
//        RichTextDomObject dom = null;
//        try {
//            dom = new RichTextDomObject();
//            copyFields(dom);
//            dom.hasBeenMeasured = hasBeenMeasured;
//            dom.atomicReference = atomicReference;
//        } catch (Exception e) {
//            if (WXEnvironment.isApkDebugable()) {
//                WXLogUtils.e("WXTextDomObject clone error: ", e);
//            }
//        }
//        if (dom != null) {
//            dom.spanned = spanned;
//        }
//        return dom;
//    }
//
//    /**
//     * RecalculateLayout.
//     */
//    private void recalculateLayout() {
//        float contentWidth = WXDomUtils.getContentWidth(this);
//        if (contentWidth > 0) {
//            mText = getText();
//            spanned = createSpanned(mText);
//            layout = createLayout(contentWidth, true, layout);
//            previousWidth = layout.getWidth();
//        }
//    }
//
//    /**
//     * Update style and text.
//     */
//    private void updateStyleAndText() {
//        updateStyleImp(getStyles());
//        mText = getText();
//    }
//
//
//    private String getText() {
//        WXComponent wxComponent = WXSDKManager.getInstance().getWXRenderManager().getWXComponent
//                (getDomContext().getInstanceId(), getRef());
//        if (wxComponent != null && wxComponent instanceof BMRich) {
//            return ((BMRich) wxComponent).getRichSpanned();
//        }
//        return null;
//    }
//
//    /**
//     * Record the property according to the given style
//     *
//     * @param style the give style.
//     */
//    private void updateStyleImp(Map<String, Object> style) {
//        if (style != null) {
//            if (style.containsKey(Constants.Name.LINES)) {
//                int lines = WXStyle.getLines(style);
//                //修改改变lines为0时的bug
//                if (lines >= 0) {
//                    mNumberOfLines = lines;
//                }
//            }
//            if (style.containsKey(Constants.Name.FONT_SIZE)) {
//                mFontSize = WXStyle.getFontSize(style, getViewPortWidth());
//            }
//            if (style.containsKey(Constants.Name.FONT_WEIGHT)) {
//                mFontWeight = WXStyle.getFontWeight(style);
//            }
//            if (style.containsKey(Constants.Name.FONT_STYLE)) {
//                mFontStyle = WXStyle.getFontStyle(style);
//            }
//            if (style.containsKey(Constants.Name.COLOR)) {
//                mColor = WXResourceUtils.getColor(WXStyle.getTextColor(style));
//                mIsColorSet = mColor != Integer.MIN_VALUE;
//            }
//            if (style.containsKey(Constants.Name.TEXT_DECORATION)) {
//                mTextDecoration = WXStyle.getTextDecoration(style);
//            }
//            if (style.containsKey(Constants.Name.FONT_FAMILY)) {
//                mFontFamily = WXStyle.getFontFamily(style);
//            }
//            mAlignment = WXStyle.getTextAlignment(style);
//            textOverflow = WXStyle.getTextOverflow(style);
//            int lineHeight = WXStyle.getLineHeight(style, getViewPortWidth());
//            if (lineHeight != UNSET) {
//                mLineHeight = lineHeight;
//            }
//        }
//    }
//
//    /**
//     * Update layout according to {@link #mText} and span
//     *
//     * @param width          the specified width.
//     * @param forceWidth     If true, force the text width to the specified width, otherwise, text
//     *                       width may equals to or be smaller than the specified width.
//     * @param previousLayout the result of previous layout, could be null.
//     */
//    private
//    @NonNull
//    Layout createLayout(float width, boolean forceWidth, @Nullable Layout previousLayout) {
//        mText = getText();
//        float textWidth;
//        textWidth = getTextWidth(mTextPaint, width, forceWidth);
//        Layout layout;
//        if (!FloatUtil.floatsEqual(previousWidth, textWidth) || previousLayout == null) {
//            layout = new StaticLayout(spanned, mTextPaint, (int) Math.ceil(textWidth),
//                    Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
//        } else {
//            layout = previousLayout;
//        }
//        if (mNumberOfLines != UNSET && mNumberOfLines > 0 && mNumberOfLines < layout.getLineCount
//                ()) {
//            int lastLineStart, lastLineEnd;
//            lastLineStart = layout.getLineStart(mNumberOfLines - 1);
//            lastLineEnd = layout.getLineEnd(mNumberOfLines - 1);
//            if (lastLineStart < lastLineEnd) {
//                String text = truncate(mText.substring(0, lastLineEnd), mTextPaint, layout
//                        .getWidth(), textOverflow);
//                spanned = createSpanned(text);
//                return new StaticLayout(spanned, mTextPaint, (int) Math.ceil(textWidth),
//                        Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
//            }
//        }
//        return layout;
//    }
//
//    @NonNull
//    private String truncate(@Nullable String source, @NonNull TextPaint paint,
//                            int desired, @Nullable TextUtils.TruncateAt truncateAt) {
//        if (!TextUtils.isEmpty(source)) {
//            StringBuilder builder;
//            Spanned spanned;
//            StaticLayout layout;
//            for (int i = source.length(); i > 0; i--) {
//                builder = new StringBuilder(i + 1);
//                builder.append(source, 0, i);
//                if (truncateAt != null) {
//                    builder.append(ELLIPSIS);
//                }
//                spanned = createSpanned(builder.toString());
//                layout = new StaticLayout(spanned, paint, desired, Layout.Alignment.ALIGN_NORMAL,
//                        1, 0, true);
//                if (layout.getLineCount() <= 3) {
//                    return spanned.toString();
//                }
//            }
//        }
//        return "";
//    }
//
//    /**
//     * Get text width according to constrain of outerWidth with and forceToDesired
//     * @param textPaint paint used to measure text
//     * @param outerWidth the width that css layout desired.
//     * @param forceToDesired if set true, the return value will be outerWidth, no matter what the
//     *                       width
//     *                   of text is.
//     * @return if forceToDesired is false, it will be the minimum value of the width of text and
//     * outerWidth in case of outerWidth is defined, in other case, it will be outer width.
//     */
//    /**
//     * package
//     **/
//    private float getTextWidth(TextPaint textPaint, float outerWidth, boolean forceToDesired) {
//        float textWidth;
//        if (forceToDesired) {
//            textWidth = outerWidth;
//        } else {
//            float desiredWidth = Layout.getDesiredWidth(spanned, textPaint);
//            if (CSSConstants.isUndefined(outerWidth) || desiredWidth < outerWidth) {
//                textWidth = desiredWidth;
//            } else {
//                textWidth = outerWidth;
//            }
//        }
//        return textWidth;
//    }
//
//    /**
//     * Update {@link #spanned} according to the give charSequence and styles
//     *
//     * @param text the give raw text.
//     * @return an Spanned contains text and spans
//     */
//    protected
//    @NonNull
//    Spanned createSpanned(String text) {
//        if (!TextUtils.isEmpty(text)) {
//            SpannableString spannable = new SpannableString(text);
//            updateSpannable(spannable, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//            return spannable;
//        }
//        return new SpannableString("");
//    }
//
//    private void updateSpannable(Spannable spannable, int spanFlag) {
//        List<SetSpanOperation> ops = createSetSpanOperation(spannable.length(),
//                spanFlag);
//        if (mFontSize == UNSET) {
//            ops.add(new SetSpanOperation(0, spannable.length(),
//                    new AbsoluteSizeSpan(WXText.sDEFAULT_SIZE), spanFlag));
//        }
//        Collections.reverse(ops);
//        for (SetSpanOperation op : ops) {
//            op.execute(spannable);
//        }
//
//        update(spannable, spanFlag);
//    }
//
//    private List<SetSpanOperation> createSetSpanOperation(int end, int spanFlag) {
//        List<SetSpanOperation> ops = new LinkedList<>();
//        int start = 0;
//        if (end >= start) {
//            if (mTextDecoration == WXTextDecoration.UNDERLINE) {
//                ops.add(new SetSpanOperation(start, end,
//                        new UnderlineSpan(), spanFlag));
//            }
//            if (mTextDecoration == WXTextDecoration.LINETHROUGH) {
//                ops.add(new SetSpanOperation(start, end,
//                        new StrikethroughSpan(), spanFlag));
//            }
//            if (mIsColorSet) {
//                ops.add(new SetSpanOperation(start, end,
//                        new ForegroundColorSpan(mColor), spanFlag));
//            }
//            if (mFontSize != UNSET) {
//                ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(mFontSize),
//                        spanFlag));
//            }
//            if (mFontStyle != UNSET
//                    || mFontWeight != UNSET
//                    || mFontFamily != null) {
//                ops.add(new SetSpanOperation(start, end,
//                        new WXCustomStyleSpan(mFontStyle, mFontWeight, mFontFamily),
//                        spanFlag));
//            }
//            ops.add(new SetSpanOperation(start, end, new AlignmentSpan.Standard(mAlignment),
//                    spanFlag));
//            if (mLineHeight != UNSET) {
//                ops.add(new SetSpanOperation(start, end, new WXLineHeightSpan(mLineHeight),
//                        spanFlag));
//            }
//        }
//        return ops;
//    }
//
//    private void update(Spannable spannable, int spanFlag) {
//        WXComponent wxComponent = WXSDKManager.getInstance().getWXRenderManager().getWXComponent
//                (getDomContext().getInstanceId(), getRef());
//        if (wxComponent != null && wxComponent instanceof BMRich) {
//            List<RichTextDomObject.BMRichSpan> spans = ((BMRich) wxComponent).getSpans();
//            for (RichTextDomObject.BMRichSpan span : spans) {
//                Log.e("rich", span.toString());
//                int start = span.getStart();
//                int end = span.getEnd();
//                if (start > spannable.length()) {
//                    start = spannable.length();
//                }
//                if (end > spannable.length()) {
//                    end = spannable.length();
//                }
//                spannable.setSpan(span.getSpan(), start, end, spanFlag);
//            }
//        }
//    }
//
//    /**
//     * Move the reference of current layout to the {@link AtomicReference} for further use, then
//     * clear current layout.
//     */
//    private void swap() {
//        if (layout != null) {
//            atomicReference.set(layout);
//            layout = null;
//            mTextPaint = new TextPaint(mTextPaint);
//        }
//    }
//
//    /**
//     * As warming up TextLayoutCache done in the DOM thread may manipulate UI operation, there may
//     * be some exception, in which case the exception is ignored. After all, this is just a warm up
//     * operation.
//     *
//     * @return false for warm up failure, otherwise returns true.
//     */
//    private boolean warmUpTextLayoutCache(Layout layout) {
//        boolean result;
//        try {
//            layout.draw(DUMMY_CANVAS);
//            result = true;
//        } catch (Exception e) {
//            WXLogUtils.eTag(TAG, e);
//            result = false;
//        }
//        return result;
//    }
//
//
//    public static class BMRichSpan {
//        private Object span;
//        private int start;
//        private int end;
//
//        public BMRichSpan(Object span, int start, int end) {
//            this.span = span;
//            this.start = start;
//            this.end = end;
//        }
//
//        public Object getSpan() {
//            return span;
//        }
//
//        public void setSpan(CharacterStyle span) {
//            this.span = span;
//        }
//
//        public int getStart() {
//            return start;
//        }
//
//        public void setStart(int start) {
//            this.start = start;
//        }
//
//        public int getEnd() {
//            return end;
//        }
//
//        public void setEnd(int end) {
//            this.end = end;
//        }
//
//        @Override
//        public String toString() {
//            return "BMRichSpan{" +
//                    "span=" + span +
//                    ", start=" + start +
//                    ", end=" + end +
//                    '}';
//        }
//    }
//}
